#import "TGIF.h"
#import "ListCell.h"
#import "ListMatrix.h"
#import "NXBitmapGraphicRep.h"
#import "PolyInspector.h"
#import "TGIFCell.h"
#import "miscutil.h"
#import <appkit/Application.h>
#import <appkit/Matrix.h>
#import <appkit/NXCursor.h>
#import <appkit/OpenPanel.h>
#import <appkit/SavePanel.h>
#import <appkit/ScrollView.h>
#import <appkit/Window.h>
#import <appkit/color.h>
#import <appkit/graphics.h>
#import <appkit/nextstd.h>
#import <stdio.h>
#import <streams/streams.h>
#import <sys/param.h>

// import command cell objects
#import "CmdBgcolor.h"
#import "CmdBgnpoly.h"
#import "CmdCircle.h"
#import "CmdComment.h"
#import "CmdDraw.h"
#import "CmdEndpoly.h"
#import "CmdFgcolor.h"
#import "CmdFps.h"
#import "CmdHold.h"
#import "CmdMove.h"
#import "CmdNewframe.h"
#import "CmdVertex.h"

#import "AbsPoly.h"

NXBitmapGraphicRep *theBitmap=nil;

@implementation TGIF
id backup;  // for undo
id openPanel, savePanel;
id pbList;  // for a "pasteboard" of cells.

BOOL getOpenPath(char *buf, char const *type1, char const *type2)
{
  char const *fileTypes[3] = {0,0,0};
  
  if (!openPanel)
      openPanel = [OpenPanel new];
  [openPanel allowMultipleFiles:NO];
  if (type1 && *type1)
      fileTypes[0] = type1;
  if (type2 && *type2)
      fileTypes[1] = type2;
  [NXApp setAutoupdate:NO];
  if ([openPanel runModalForDirectory:(const char*)buf
   file:"" types:fileTypes]) {
    strcpy(buf,[openPanel filename]);
    [NXApp setAutoupdate:YES];
    return YES;
  } else {
    [NXApp setAutoupdate:YES];
    return NO;
  }
}

BOOL getSavePath(char *returnBuf, char *dir, char *name, char const *theType)
{
  if (!savePanel) {
    savePanel = [SavePanel new];
   [savePanel setTitle:"GraphicsWrap Save"];
  }
  if (theType && *theType)
      [savePanel setRequiredFileType:theType];
  [NXApp setAutoupdate:NO];
  if ([savePanel runModalForDirectory:dir file:name]) {
    strcpy(returnBuf,[savePanel filename]);
    [NXApp setAutoupdate:YES];
    return YES;
  } else {
    [NXApp setAutoupdate:YES];
    return NO;
  }
}

void parsePath(char *path, char *dir, char *name)
{
  char *c1, *c2;
  int dirlen, namelen;
  
  c1 = strrchr(path, '.');
  c2 = strrchr(path, '/');
  dirlen = (c2) ? c2-path+1 : 0;
  namelen = ((c1) ? c1-path : strlen(path)) -dirlen;
  strncpy(dir,path,dirlen);
  dir[dirlen] = '\0';
  strncpy(name, path+dirlen, namelen);
  name[namelen] = '\0';
}

void getPath(char *path, char *dir, char *name, char *ext)
     /* construct a path given a file name, directory, and type */
{
  strcpy(path,(dir)?dir:NXHomeDirectory());
  if (name) {
    strcat(path,name);
    if (strlen(ext) && (ext[0] != '.'))
	strcat(path,".");
    strcat(path,ext);
  }
}

- displayMatrix:aMatrix
{
  [aMatrix sizeToCells];
  [aMatrix display];
  return self;
}

- initMatrix
{
  NXRect aRect, bRect;
  NXSize cellSpacing={0.0,0.0}, cellSize;
  
  [tgif_view getFrame:&aRect];
  [ScrollView getContentSize:&(bRect.size) forFrameSize:&(aRect.size)
   horizScroller:YES vertScroller:YES borderType:NX_BEZEL];
  
  tgif_matrix = [[ListMatrix alloc] initFrame:&bRect mode:NX_LISTMODE
		   cellClass:[TGIFCell class] numRows:0 numCols:1];
  [tgif_matrix setIntercell:&cellSpacing];
  [tgif_matrix allowEmptySel:YES];
  [tgif_matrix setControlDrag:YES];
  
  [tgif_matrix getCellSize:&cellSize];
  cellSize.width = NX_WIDTH(&bRect);
  [tgif_matrix setCellSize:&cellSize];
  [tgif_matrix sizeToCells];
  [tgif_matrix setAutosizeCells:YES];
  [tgif_matrix setAutoscroll:YES];
  
  [tgif_view setDocView:tgif_matrix];
  [[tgif_matrix superview] setAutoresizeSubviews:YES];
  [tgif_matrix setAutosizing:NX_WIDTHSIZABLE];
  
  [tgif_matrix setBackgroundGray:NX_LTGRAY];
  [tgif_matrix setCellBackgroundGray:NX_LTGRAY];
  [tgif_view setBackgroundGray:NX_LTGRAY];
  
  [tgif_view setDocCursor:NXArrow];
  [tgif_matrix setTarget:self];
  [tgif_matrix setAction:@selector(tgif_click:)];
  [self displayMatrix:tgif_matrix];
  return self;
}

- initWithBitmap:abp andView:av;
{
  char title[100];
  [super init];
  [NXApp loadNibSection:"TGIF.nib" owner:self withNames:NO];
  theBitmap=abp;
  bitmap_view=av;

  poly_insp=[[PolyInspector alloc] init];

  NX_MALLOC(filePath,char,MAXPATHLEN+1);
  NX_MALLOC(fileName,char,MAXPATHLEN+1);
  NX_MALLOC(fileDir,char,MAXPATHLEN+1);

  sprintf(title,"UNTITLED.tgif");
  [tgif_window setTitleAsFilename:(const char*)title];
  NXPing();
  
  sprintf(filePath,"~/");
  sprintf(fileName,title);
  sprintf(fileDir,"~/");

  [self initMatrix];

  return self;
}


- perform:sender
{
  id cellList=[tgif_matrix cellListIncludeAll:NO];
  int i, max=[cellList count];

  if(!max)cellList=[tgif_matrix cellListIncludeAll:YES];
  for(i=0;i<max;i++){
    [[cellList objectAt:i] doCmd];
    [bitmap_view display];
  }
  return self;
}

- tgif_click:sender
{
  id tList=[tgif_matrix cellListIncludeAll:NO];
  
  if(([tList count] == 1) && [[tList objectAt:0] isMemberOf:[AbsPoly class]]){
    [poly_insp inspect:[tList objectAt:0]];
    [[poly_insp window] makeKeyAndOrderFront:self];
  } else [[poly_insp window] orderOut:self];
  return self;
}

- saveToFile
{
  int i;
  id cmdList;
  NXStream *theStream;
  
  if (!getSavePath(filePath,fileDir,fileName,"tgif"))
      return self;
  parsePath(filePath, fileDir, fileName);
  theStream=NXOpenMemory(NULL, 0, NX_WRITEONLY);
  cmdList=[tgif_matrix cellListIncludeAll:YES];
  for(i=0;i<[cmdList count];i++)
    NXPrintf(theStream, "%s", [[cmdList objectAt:i] emitCmd]);
  NXFlush(theStream);
  NXSaveToFile(theStream, filePath);
  NXClose(theStream);
  return self;
}

- addRowTo:newCell
{
  int rc, cc;
  [tgif_matrix getNumRows:&rc numCols:&cc];
  [tgif_matrix addRow];
  [tgif_matrix putCell:newCell at:rc :0];  
  return self;
}

id curPoly;
BOOL polyMode;
- addActiveCmd:(char *)cmd
{
  long arg1=-1, arg2=-1, arg3=-1;
  int cc=0, argnum=0;
  char *c[3];

  c[0]=c[1]=c[2]=(char *)nil;
  while(cmd[cc]!='\0'){
    if(cmd[cc]==' '){
      cmd[cc]='\0';
      c[argnum++]=&cmd[++cc];
    } else cc++;
  }

  if(c[0]) arg1=atol(c[0]);
  if(c[1]) arg2=atol(c[1]);
  if(c[2]) arg3=atol(c[2]);

  if (polyMode){
    // check for "endpoly"
    if(!strcmp(cmd, "endpoly") || !strcmp(cmd,"ep")){
      [curPoly addCommand:[[CmdEndpoly alloc] initCmd]];
      polyMode=FALSE;
    }
    //check for "vertex"
    else if(!strcmp(cmd, "vertex") || !strcmp(cmd,"v"))
      [curPoly addCommand:[[CmdVertex alloc] initCmd:arg1 :arg2]];
    else fprintf(stderr, "Illegal token in PolyGon definition:%s %d %d %d\n",
		 cmd, arg1, arg2, arg3);
  } else {  
    // check for "move"
    if(!strcmp(cmd, "move") || !strcmp(cmd,"m"))
      [self addRowTo:[[CmdMove alloc] initCmd:arg1 :arg2]];
    // check for "draw"
    else if(!strcmp(cmd, "draw") || !strcmp(cmd,"d"))
      [self addRowTo:[[CmdDraw alloc] initCmd:arg1 :arg2]];
    // check for "circle"
    else if(!strcmp(cmd, "circle") || !strcmp(cmd,"c"))
      [self addRowTo:[[CmdCircle alloc] initCmd:arg1]];
    //check for "fgcolor"
    else if(!strcmp(cmd, "fgcolor") || !strcmp(cmd,"fg"))
      [self addRowTo:[[CmdFgcolor alloc] initCmd:arg1 :arg2 :arg3]];
    // check for "bgcolor"
    else if(!strcmp(cmd, "bgcolor") || !strcmp(cmd,"bg"))
      [self addRowTo:[[CmdBgcolor alloc] initCmd:arg1 :arg2 :arg3]];
    // check for "bgnpoly"
    else if(!strcmp(cmd, "bgnpoly") || !strcmp(cmd,"bp")){
      curPoly=[[AbsPoly alloc] init];
      [self addRowTo:curPoly];
      [curPoly addCommand:[[CmdBgnpoly alloc] initCmd]];
      polyMode=TRUE;
    }
    // check for "newframe"
    else if(!strcmp(cmd, "newframe") || !strcmp(cmd,"n"))
      [self addRowTo:[[CmdNewframe alloc] initCmd]];
    // check for "fps"
    else if(!strcmp(cmd, "fps"))
      [self addRowTo:[[CmdFps alloc] initCmd:arg1]];
    // check for "hold"
    else if(!strcmp(cmd, "hold"))
      [self addRowTo:[[CmdHold alloc] initCmd:arg1]];
    else fprintf(stderr, "Bad Token (ignore -1): %s %d %d %d\n",
		 cmd, arg1, arg2, arg3);
  }
  
  return self;
}

- openFile
{
  char path[MAXPATHLEN+1], cmd[2000];
  char c;
  int rc, cc;
  NXStream *theStream;
  if (getOpenPath(path,"tgif",fileDir)) {
    [tgif_matrix getNumRows:&rc numCols:&cc];
    for(cc=0;cc<rc;cc++)
      [tgif_matrix removeRowAt:cc andFree:YES];
    theStream=NXMapFile(path, NX_READONLY);

    cc=0;
    while((c=NXGetc(theStream))!=EOF){
      cmd[cc]=c;
      if(c=='\n'){
	cmd[cc]='\0';
	cc=0;
	if(cmd[0]=='#') {
	  // this is a comment line
	  [self addRowTo:[[CmdComment alloc] initComment:cmd]];
	} else
	  // this is really a command, process it as one
	  [self addActiveCmd:cmd];
      } else cc++;
    }
    NXClose(theStream);
  }
  [self displayMatrix:tgif_matrix];
  strcpy(filePath, path);
  parsePath(filePath, fileDir, fileName);
  [tgif_window setTitleAsFilename:(const char*)filePath];
  return self;
}

- newFile
{
  char title[100];
  int i, rc, cc;
  [tgif_matrix getNumRows:&rc numCols:&cc];
  for(i=0;i<rc;i++)
    [tgif_matrix removeRowAt:0 andFree:YES];
  [self displayMatrix:tgif_matrix];
  
  sprintf(title,"UNTITLED.tgif");
  [tgif_window setTitleAsFilename:(const char*)title];
  NXPing();
  
  sprintf(filePath,"~/");
  sprintf(fileName,title);
  sprintf(fileDir,"~/");
  return self;
}


- cmdMove:(int)x :(int)y
{
  [self addRowTo:[[CmdMove alloc] initCmd:x :y]];
  [self displayMatrix:tgif_matrix];
  return self;
}

- cmdDraw:(int)x :(int)y
{
  [self addRowTo:[[CmdDraw alloc] initCmd:x :y]];
  [self displayMatrix:tgif_matrix];
  return self;
}

-cmdNewFrame
{
  [self addRowTo:[[CmdNewframe alloc] initCmd]];
  [self displayMatrix:tgif_matrix];
  return self;
}

- cmdForeColor:(NXColor)aColor
{
  [self addRowTo:[[CmdFgcolor alloc] initCmdColor:aColor]];
  [self displayMatrix:tgif_matrix];
  return self;
}

- cmdBackColor:(NXColor)aColor
{
  [self addRowTo:[[CmdBgcolor alloc] initCmdColor:aColor]];
  [self displayMatrix:tgif_matrix];
  return self;
}


- cmdComment:(char *)comment
{
  char *tmp;
  tmp=(char *) malloc((strlen(comment)+3)*sizeof(char));
  sprintf(tmp, "#%s\n", comment);
  [self addRowTo:[[CmdComment alloc] initComment:tmp]];
  free(tmp);
  return self;
}

- cmdNewPoly
{
  // create new polygon
  id aPoly=[[AbsPoly alloc] init];

  // add polygon to our matrix
  [self addRowTo:aPoly];
  [self displayMatrix:tgif_matrix];
  
  // return polygon to caller for vertex addition
  return aPoly;
}

// through first responder
- delete:sender
{
  id cellList;
  int i, rc, cc;
  cellList=[tgif_matrix cellListIncludeAll:NO];
  for(i=0;i<[cellList count];i++){
    [tgif_matrix getRow:&rc andCol:&cc ofCell:[cellList objectAt:i]];
    [tgif_matrix removeRowAt:rc andFree:NO];
  }
  [cellList freeObjects];
  [self displayMatrix:tgif_matrix];
  return self;
}

- cut:sender
{
  int i, rc, cc;
  if(pbList) [pbList free]; else pbList=[[List alloc] init];
  pbList=[tgif_matrix cellListIncludeAll:NO];
  for(i=0;i<[pbList count];i++){
    [tgif_matrix getRow:&rc andCol:&cc ofCell:[[pbList objectAt:i] copy]];
    [tgif_matrix removeRowAt:rc andFree:YES];
  }
  [self displayMatrix:tgif_matrix];
  return self;
}
@end
